/*
 * Marmoset Add-In for Microsoft Visual Studio
 *
 * Copyright (C) 2007, David H. Hovemeyer
 * Copyright (C) 2007, Fidel K. Estrella
 *
 * This program is free software licensed under the
 * terms of the GNU General Public License, version 2.
 * See the file "COPYING.txt" for details.
 */

using System;
using System.Collections;
using System.IO;
using System.Text;
using Extensibility;
using EnvDTE;
using EnvDTE80;

namespace Marmoset
{
    /// <summary>
    /// SolutionFileCollector collects all of the files that are
    /// part of the currently-open solution.
    /// </summary>
    class SolutionFileCollector
    {
        #region Private Fields
        private Solution _solution;
        private Hashtable _inputFileSet;
        private DebugOutput _debugOut;
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="solution">the Solution whose files should be collected</param>
        public SolutionFileCollector(Solution solution)
        {
            this._solution = solution;
            this._inputFileSet = new Hashtable();
            this._debugOut = null;
        }
        #endregion

        #region Public Members
        /// <summary>
        /// Set DebugOutput object to which debug messages should be sent.
        /// </summary>
        /// <param name="debugOutput"></param>
        public void SetDebugOutput(DebugOutput debugOutput)
        {
            this._debugOut = debugOutput;
        }

        /// <summary>
        /// Collect all of the solution files.
        /// </summary>
        public void CollectSolutionFiles()
        {
            //Add solution file to zip
            AddFile(_solution.FullName);

            //Iterate through each project and archive its contents
            for (IEnumerator e = _solution.Projects.GetEnumerator(); e.MoveNext(); )
            {
                //Find each project in the solution
                Project proj = (Project)e.Current;
                AddProjectFiles(proj);
            }

            WriteToOutput("Found " + _inputFileSet.Keys.Count + " files altogether");
        }

        /// <summary>
        /// Get the collection of filenames of all files that are
        /// part of the solution.
        /// </summary>
        /// <returns>collection of files that are part of the solution</returns>
        public ICollection GetFileCollection()
        {
            return _inputFileSet.Keys;
        }

        /// <summary>
        /// Get the pathnaem of the base directory of the solution.
        /// All of the collected files are in this directory or some subdirectory.
        /// Therefore, it can be used as the base directory for
        /// creating a zipfile of the solution files.
        /// </summary>
        /// <returns>base directory of the solution</returns>
        public string GetBaseDirectory()
        {
            string solutionBaseDir = Path.GetDirectoryName(_solution.FullName);
            return solutionBaseDir;
        }
        #endregion

        #region Private Members
        private void AddFile(string fileName)
        {
            if (!_inputFileSet.ContainsKey(fileName))
            {
                WriteToOutput("Adding file " + fileName + " to solution files");
                _inputFileSet.Add(fileName, 1);
            }
        }

        private void AddProjectFiles(Project proj)
        {
            WriteToOutput("Found project: [" + proj.FullName + "]");

            // Sometimes the project's name is not a valid file name
            if (File.Exists(proj.FullName))
            {
                // Add the filename of the project itself
                AddFile(proj.FullName);
            }

            //Find all items (source files, text files, etc.) in a project and enumerate

            // Seed worklist with top-level ProjectItems for this Project
            ArrayList workList = new ArrayList();
            {
                IEnumerator e = proj.ProjectItems.GetEnumerator();
                while (e.MoveNext())
                {
                    ProjectItem item = (ProjectItem)e.Current;
                    WriteToOutput("Adding " + item.Name + " to work list");
                    workList.Add(item);
                }
            }

            // Discover all filenames associated with all ProjectItems
            while (workList.Count > 0)
            {
                // Remove first worklist item
                ProjectItem item = (ProjectItem)workList[0];
                workList.RemoveAt(0);

                WriteToOutput("Trying to get files associated with item " + item.Name);
                WriteToOutput("Item reports FileCount of " + item.FileCount);

                // Add all files directly associated with this ProjectItem
                for (short i = 0; i <= item.FileCount; i++)
                {
                    // Microsoft can't seem to decide whether index values start at 0 or 1,
                    // so we have to accomodate.
                    try
                    {
                        string fileName = item.get_FileNames(i);
                        if (File.Exists(fileName) && !Directory.Exists(fileName))
                        {
                            AddFile(fileName);
                        }
                    }
                    catch (ArgumentException /*e*/)
                    {
                        // Ignore
                    }
                }

                // Add any child ProjectItems to the worklist
                if (item.ProjectItems != null)
                {
                    IEnumerator e = item.ProjectItems.GetEnumerator();
                    while (e.MoveNext())
                    {
                        ProjectItem subItem = (ProjectItem)e.Current;
                        WriteToOutput("Adding " + subItem.Name + " to work list (recursively)");
                        workList.Add(subItem);
                    }
                }
            }
        }

        private void WriteToOutput(string message)
        {
            if (_debugOut != null)
            {
                _debugOut.WriteToOutput(message);
            }
        }
        #endregion
    }
}
